package fr.noe.amicaleloisirs.concours.utils;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.xml.client.Document;
import com.google.gwt.xml.client.Element;
import com.google.gwt.xml.client.Node;
import com.google.gwt.xml.client.NodeList;
import com.google.gwt.xml.client.Text;
import com.google.gwt.xml.client.XMLParser;
import com.google.gwt.xml.client.impl.DOMParseException;

import fr.noe.amicaleloisirs.concours.business.Concours;
import fr.noe.amicaleloisirs.concours.business.Match;
import fr.noe.amicaleloisirs.concours.business.Player;
import fr.noe.amicaleloisirs.concours.business.Team;
import fr.noe.amicaleloisirs.concours.business.reloadExisting.ConcoursExistant;
import fr.noe.amicaleloisirs.concours.business.reloadExisting.MatchExistant;
import fr.noe.amicaleloisirs.concours.business.reloadExisting.TeamExistant;
import fr.noe.amicaleloisirs.concours.view.InfoPopUp;

public class XMLHelper {

	private static XMLHelper instance = null;
	private Document listeJoueurs = null;

	public static String XML_TRUE = "1";
	public static String XML_FALSE = "0";

	public static XMLHelper get() {
		if (instance == null)
			instance = new XMLHelper();
		return instance;
	}

	private boolean isNaN(String text) {
		return (text == null || text.isEmpty() || "null".equals(text) || "undefined"
				.equals(text));
	}

	/**
	 * @return initialise l'helper avec la liste XML des joueurs.
	 */
	public boolean setListeJoueurs(String docXml) {
		if (isNaN(docXml)) {
			return createPlayersListFile();
		} else {
			try {
				listeJoueurs = XMLParser.parse(docXml);
				return true;
			} catch (DOMParseException e) {
				return false;
			}
		}
	}

	/**
	 * @return le joueur dont l'id est celui en parametre.
	 */
	public Player getPlayerById(int id) {
		Element player = (Element) listeJoueurs.getElementsByTagName("joueur")
				.item(id);
		if ((player != null) && ("" + id).equals(player.getAttribute("id")))
			return new Player(player.getAttribute("prenom"),
					player.getAttribute("nom"), id);
		else {
			NodeList list = listeJoueurs.getElementsByTagName("joueur");
			for (int i = 0; i < list.getLength(); i++) {
				player = (Element) list.item(i);
				if (("" + id).equals(player.getAttribute("id")))
					return new Player(player.getAttribute("prenom"),
							player.getAttribute("nom"), id);
			}
		}
		return null;
	}

	/**
	 * Retourne la liste des joueurs.
	 */
	public List<Player> getPlayerList() {
		List<Player> res = new ArrayList<Player>();
		int nbtotal = getNbTotalPlayer();
		for (int i = 0; i < nbtotal; i++) {
			res.add(getPlayerById(i));
		}
		return res;
	}

	/**
	 * Retourne le nombre de joueurs.
	 */
	public int getNbTotalPlayer() {
		Node nbTotal = listeJoueurs.getElementsByTagName("nbtotal").item(0);
		String stringId = ((Element) nbTotal).getFirstChild().getNodeValue();
		return Integer.parseInt(stringId);
	}

	/**
	 * Cree un nouveau joueur. L'helper contient la reference du nouveau joueur,
	 * et le fichier XML est mis à jours.
	 */
	public Player createPlayer(String nom, String prenom) {
		Node nbTotal = listeJoueurs.getElementsByTagName("nbtotal").item(0);
		String stringId = ((Element) nbTotal).getFirstChild().getNodeValue();
		int id = Integer.parseInt(stringId);

		// create xml player
		Node listeElement = listeJoueurs.getElementsByTagName("liste").item(0);
		Element joueurACreer = listeJoueurs.createElement("joueur");
		joueurACreer.setAttribute("id", stringId);
		joueurACreer.setAttribute("nom", nom);
		joueurACreer.setAttribute("prenom", prenom);
		listeElement.appendChild(joueurACreer);
		((Element) nbTotal).getFirstChild().setNodeValue("" + (id + 1));

		// save
		FileSystem.get().write(Constants.PLAYER_LIST_FILE_URL,
				listeJoueurs.toString());

		return new Player(prenom, nom, id);
	}

	/**
	 * Cree le fichier "vide" (sans joueurs)
	 */
	public boolean createPlayersListFile() {
		if (listeJoueurs == null) {
			Document doc = XMLParser.createDocument();
			Element joueurs = doc.createElement("joueurs");
			Element nbTotal = doc.createElement("nbtotal");
			Text text = doc.createTextNode("0");
			nbTotal.appendChild(text);
			Element liste = doc.createElement("liste");
			joueurs.appendChild(nbTotal);
			joueurs.appendChild(liste);
			doc.appendChild(joueurs);
			listeJoueurs = doc;
		}
		return FileSystem.get().write(Constants.PLAYER_LIST_FILE_URL,
				listeJoueurs.toString());
	}

	/**
	 * Modifie un joueur et la sauve.
	 */
	public boolean updatePlayer(Player p) {
		Element player = (Element) listeJoueurs.getElementsByTagName("joueur")
				.item(p.getId());
		if ((player != null)
				&& ("" + p.getId()).equals(player.getAttribute("id"))) {
			player.removeAttribute("nom");
			player.setAttribute("nom", p.getLastname());
			player.removeAttribute("prenom");
			player.setAttribute("prenom", p.getFirstname());
		} else {
			NodeList list = listeJoueurs.getElementsByTagName("joueur");
			for (int i = 0; i < list.getLength(); i++) {
				player = (Element) list.item(i);
				if (("" + p.getId()).equals(player.getAttribute("id"))) {
					player.removeAttribute("nom");
					player.setAttribute("nom", p.getLastname());
					player.removeAttribute("prenom");
					player.setAttribute("prenom", p.getFirstname());
				}
			}
		}
		return FileSystem.get().write(Constants.PLAYER_LIST_FILE_URL,
				listeJoueurs.toString());
	}

	private void savePlayer(Player p, Element elem) {
		elem.setAttribute("id", "" + p.getId());
		elem.setAttribute("nom", p.getLastname());
		elem.setAttribute("prenom", p.getFirstname());
	}

	private void saveEqRencontrees(List<Integer> list, Element elem,
			Document doc) {
		for (Integer i : list) {
			Element team = doc.createElement("equipeRencontree");
			team.setAttribute("id", "" + i);
			elem.appendChild(team);
		}
	}

	private void saveTeam(Team t, Node parentTeamNode, Document doc) {
		Element teamASauver = doc.createElement("equipe");
		teamASauver.setAttribute("teamId", "" + t.getTeamId());
		teamASauver.setAttribute("serie", t.getSerie());
		teamASauver.setAttribute("nbVictoire", "" + t.getNombreVictoires());
		teamASauver.setAttribute("nbDefaite", "" + t.getNombreDefaites());
		teamASauver.setAttribute("totalPour", "" + t.getTotalPour());
		teamASauver.setAttribute("totalContre", "" + t.getTotalContre());
		teamASauver.setAttribute("alreadyExempt",
				t.hasAlreadyBeenExempt() ? XML_TRUE : XML_FALSE);
		Element dejaRenc = doc.createElement("dejaRencontres");
		saveEqRencontrees(t.getDejaRencontres(), dejaRenc, doc);
		teamASauver.appendChild(dejaRenc);
		Element p1 = doc.createElement("p1");
		savePlayer(t.getPlayer1(), p1);
		Element p2 = doc.createElement("p2");
		savePlayer(t.getPlayer2(), p2);
		teamASauver.appendChild(p1);
		teamASauver.appendChild(p2);
		parentTeamNode.appendChild(teamASauver);
	}

	private void saveMatch(Match m, Node parentMatchNode, Document doc) {
		Element matchASauver = doc.createElement("match");
		if (m.getT1() == null)
			matchASauver.setAttribute("t1", "-1");
		else
			matchASauver.setAttribute("t1", "" + m.getT1().getTeamId());
		if (m.getT2() == null)
			matchASauver.setAttribute("t2", "-1");
		else
			matchASauver.setAttribute("t2", "" + m.getT2().getTeamId());
		parentMatchNode.appendChild(matchASauver);
	}

	/**
	 * Sauvegarde le concours dans le fichier spécifié.
	 * 
	 * @param filename
	 *            : filename du fichier dans lequel sauver le concours.
	 * @param c
	 *            : le concours à sauver
	 * @return true si sauvegarde ok.
	 */
	public boolean saveConcours(String filename, Concours c) {
		log("saveConcours() start");
		try {
			String old = filename.substring(0,
					filename.length() - ".xml".length());
			if (FileSystem.get().fileExists(filename)) {
				log("saveConcours() concours existant, faire une copie");
				
				if (FileSystem.get().fileExists(old + "_old.xml"))
					FileSystem.get().fileDelete(old + "_old.xml");

				// copie temporaire à garder si echec de la sauvegarde
				boolean moved = FileSystem.get().move(filename, old + "_old.xml");
				log("saveConcours() concours copié : " + moved);
			}

			Document doc = XMLParser.createDocument();
			Element concours = doc.createElement("concours");
			concours.setAttribute("date", c.getDate());
			concours.setAttribute("tour", "" + c.getIntTour());
			for (Team t : c.getTeams()) {
				saveTeam(t, concours, doc);
			}
			for (Match m : c.getMatches()) {
				saveMatch(m, concours, doc);
			}
			doc.appendChild(concours);
			
			boolean saveOk = FileSystem.get().write(filename, doc.toString());
			log("saveConcours() concours sauvé : " + saveOk);
			if (!saveOk) {
				FileSystem.get().move(old + "_old.xml", filename);
			}
			return saveOk;
		} catch (Exception e) {
			log("saveConcours() erreur : " + e.getMessage());
		}
		return false;
	}

	/**
	 * Charger un concours déjà existant en populant l'instance du concours.
	 * 
	 * @param filename
	 *            : le filename du fichier dans lequel est sauvé le concours.
	 * @return l'instance du {@link Concours}.
	 */
	public Concours loadConcours(String filename) {
		Document doc = null;
		try {
			String concoursStream = FileSystem.get().read(filename);
			doc = XMLParser.parse(concoursStream);
			if (doc == null)
				throw new DOMParseException();
		} catch (Exception e) {
			new InfoPopUp("Impossible de charger la sauvegarde du concours",
					new ClickHandler() {

						@Override
						public void onClick(ClickEvent event) {
						}

					});
		}
		Element concours = (Element) doc.getElementsByTagName("concours").item(0);
		String date = concours.getAttribute("date");
		
		List<Team> teams = new ArrayList<Team>();
		NodeList listEquipes = concours.getElementsByTagName("equipe");
		for (int i = 0 ; i < listEquipes.getLength() ; i++) {
			Element equipe = (Element) listEquipes.item(i);
			
			NodeList list = equipe.getChildNodes();
			Player p1 = null;
			Player p2 = null;
			List<String> dejaRencontres = new ArrayList<String>();
			
			for (int j = 0 ; j < list.getLength() ; j++) {
				Node node = list.item(j);
				if ("p1".equals(node.getNodeName())) {
					p1 = new Player(((Element) node).getAttribute("prenom"), ((Element) node).getAttribute("nom"), Integer.parseInt(((Element) node).getAttribute("id")));
				}
				else if ("p2".equals(node.getNodeName())) {
					p2 = new Player(((Element) node).getAttribute("prenom"), ((Element) node).getAttribute("nom"), Integer.parseInt(((Element) node).getAttribute("id")));
				}
				else {
					// dejaRencontres
					NodeList eqRenc = node.getChildNodes();
					for (int er = 0 ; er < eqRenc.getLength() ; er++) {
						dejaRencontres.add(((Element)eqRenc.item(er)).getAttribute("id"));
					}
				}
			}
			teams.add(new TeamExistant(p1, p2, equipe.getAttribute("teamId"), equipe.getAttribute("serie"), equipe.getAttribute("nbVictoire"), 
					equipe.getAttribute("nbDefaite"), equipe.getAttribute("totalPour"), equipe.getAttribute("totalContre"), equipe.getAttribute("alreadyExempt"), dejaRencontres));
		}
		int tour = Integer.parseInt(concours.getAttribute("tour"));
		
		List<MatchExistant> matchs = new ArrayList<MatchExistant>();
		NodeList listMatchs = doc.getElementsByTagName("match");
		
		for (int i = 0 ; i < listMatchs.getLength() ; i++) {
			Element match = (Element) listMatchs.item(i);
			matchs.add(new MatchExistant(match.getAttribute("t1"), match.getAttribute("t2")));
		}
		return new ConcoursExistant(date, teams, tour, matchs);
	}
	
	private static void log(String text) {
		Logger.get().logWithClass("XMLHelper", text);
	}

}
